/*
 * Copyright 2008 the Breachwalls OSS project and the Others.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package com.breachwalls.mogen.generator.template;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Java の一般的な規約に則ってソートする機能を持つ、インポートコンテキスト.
 * @author todoken
 * @version $Revision: 31 $
 */
public class SortableImportContext implements ImportContext {

    /**
     * シリアルバージョンID.
     */
    private static final long serialVersionUID = 5822111642843957739L;

    /**
     * ロガー.
     */
    private static Logger logger = LoggerFactory.getLogger(ImportContext.class);

    /**
     * インポート文を保存するストレージ. {@link java.lang.Set}を使用しているため、重複は除外される.
     */
    private Set<String> imports = new HashSet<String>();

    /**
     * インポートするクラスをコンテキストに追加する.
     * @param classname インポートするクラス
     */
    public void add(final String classname) {
        if (StringUtils.isNotBlank(classname) && !StringUtils.startsWith(classname, "java.lang")) {
            imports.add(classname);
        }
    }

    /**
     * インポート文用にソート済みの文字配列を取得する.
     * @return インポート文用にソート済みの文字配列
     */
    public String[] toArray() {
        String[] values = imports.toArray(new String[imports.size()]);
        Arrays.sort(values, new ImportElementsComparator());

        List<String> imp = new ArrayList<String>();
        String beforeX = null;
        for (String v : values) {
            String thisX = StringUtils.split(v, '.')[0];
            if (beforeX != null && !StringUtils.equals(thisX, beforeX)) {
                imp.add("");
            }
            beforeX = thisX;
            imp.add(v);
        }

        return imp.toArray(new String[imp.size()]);
    }

    /**
     * インポートコンテキストの内容を全てクリアする.
     */
    public void clear() {
        imports.clear();
    }

    /**
     * インポート文用のコンパレータ.<br>
     * java, javax, org, com, net...の順にインポートクラスをソートする.
     * @author todoken
     * @version $Revision: 31 $
     */
    static class ImportElementsComparator implements Comparator<String>, Serializable {

        /**
         * シリアルバージョンID.
         */
        private static final long serialVersionUID = -6467658232550915628L;

        /**
         * {@inheritDoc}
         */
        public int compare(final String o1, final String o2) {
            int compare = o1.compareTo(o2);
            final ImportElements elm1 = toImportSentence(o1);
            final ImportElements elm2 = toImportSentence(o2);
            if (elm1 != null) {
                if (elm2 != null) {
                    if (elm1 != elm2) {
                        compare = elm1.compareTo(elm2);
                    }
                } else {
                    compare = -1;
                }
            } else {
                if (elm2 != null) {
                    compare = 1;
                }
            }
            return compare;
        }

        /**
         * クラス名をインポート文用の列挙型に変換する.
         * @param classname クラス名
         * @return インポート文列挙型
         */
        ImportElements toImportSentence(final String classname) {
            ImportElements sentence = null;
            try {
                if (StringUtils.isNotBlank(classname)) {
                    String[] v = StringUtils.split(classname, '.');
                    sentence = ImportElements.valueOf(StringUtils.upperCase(v[0]));
                }
            } catch (IllegalArgumentException e) {
                if (logger.isDebugEnabled()) {
                    logger.debug("[" + classname + "] is not sortable sentence.");
                }
            }
            return sentence;
        }

        /**
         * インポート文列挙型.
         * @author todoken
         * @version $Revision: 31 $
         */
        enum ImportElements {

            /**
             * javaで始まるインポート文.
             */
            JAVA,

            /**
             * javaxで始まるインポート文.
             */
            JAVAX,

            /**
             * javaで始まるインポート文.
             */
            ORG,

            /**
             * comで始まるインポート文.
             */
            COM,

            /**
             * netで始まるインポート文.
             */
            NET;

        }

    }

}
